// GeoServiceImpl.java
package net.jgrm.redisadvanced.service.impl;

import net.jgrm.redisadvanced.service.RedisGeoService;
import org.springframework.data.geo.Circle;
import org.springframework.data.geo.Distance;
import org.springframework.data.geo.GeoResults;
import org.springframework.data.geo.Point;
import org.springframework.data.redis.connection.RedisGeoCommands;
import org.springframework.data.redis.core.GeoOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.domain.geo.Metrics;

import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * Redis GEO 地理位置服务实现类
 */
//@Service
public class RedisGeoServiceImpl<T> implements RedisGeoService<T> {

    //    @Autowired
    private RedisTemplate<String, T> redisTemplate;

    public RedisGeoServiceImpl(RedisTemplate<String, T> redisTemplate) {
        this.redisTemplate = redisTemplate;
    }

    @Override
    public Long addLocation(String key, T member, double longitude, double latitude) {
        GeoOperations<String, T> geoOps = redisTemplate.opsForGeo();
        return geoOps.add(key, new Point(longitude, latitude), member);
    }

    @Override
    public Long addLocations(String key, Map<T, Point> locations) {
        GeoOperations<String, T> geoOps = redisTemplate.opsForGeo();
        return geoOps.add(key, locations);
    }

    @Override
    public List<Point> getLocations(String key, T... members) {
        GeoOperations<String, T> geoOps = redisTemplate.opsForGeo();
        return geoOps.position(key, members);
    }

    @Override
    public Distance getDistance(String key, T member1, T member2, Metrics metric) {
        GeoOperations<String, T> geoOps = redisTemplate.opsForGeo();
        return geoOps.distance(key, member1, member2, metric);
    }


    @Override
    public GeoResults<RedisGeoCommands.GeoLocation<T>> getNearbyLocations(
            String key, double longitude, double latitude, double radius,
            Metrics metric, long count) {
        GeoOperations<String, T> geoOps = redisTemplate.opsForGeo();


        // 经过测试两个.includeDistance() .includeCoordinates() 不能同时指定，同时指定的时候距离就没有返回
        // 如果两个都不指定的时候，默认返回距离
        RedisGeoCommands.GeoRadiusCommandArgs args = RedisGeoCommands.GeoRadiusCommandArgs.newGeoRadiusArgs()
//                .includeDistance()
//                .includeCoordinates()
                .sortAscending()
                .limit(count);
        Circle circle = new Circle(new Point(longitude, latitude), new Distance(radius, metric));
        return geoOps.radius(key, circle, args);
    }

    @Override
    public GeoResults<RedisGeoCommands.GeoLocation<T>> getNearbyLocations(
            String key, T member, double radius,
            Metrics metric, long count) {
        GeoOperations<String, T> geoOps = redisTemplate.opsForGeo();
        // // 经过测试两个.includeDistance() .includeCoordinates() 不能同时指定，同时指定的时候距离就没有返回
        // 如果两个都不指定的时候，默认返回距离
        RedisGeoCommands.GeoRadiusCommandArgs args = RedisGeoCommands.GeoRadiusCommandArgs.newGeoRadiusArgs()
//                .includeDistance()
//                .includeCoordinates()
                .sortAscending()
                .limit(count);
        Distance distance = new Distance(radius, metric);
        return geoOps.radius(key, member, distance, args);
    }

    @Override
    public List<T> getNearbyLocationNames(String key, double longitude, double latitude,
                                          double radius, Metrics metric) {
        GeoOperations<String, T> geoOps = redisTemplate.opsForGeo();

        RedisGeoCommands.GeoRadiusCommandArgs args = RedisGeoCommands.GeoRadiusCommandArgs.newGeoRadiusArgs()
                .sortAscending();
        Circle circle = new Circle(new Point(longitude, latitude), new Distance(radius, RedisGeoCommands.DistanceUnit.METERS));
        GeoResults<RedisGeoCommands.GeoLocation<T>> results =
                geoOps.radius(key, circle, args);
        return results.getContent().stream()
                .map(result -> result.getContent().getName())
                .collect(Collectors.toList());
    }

    @Override
    public Long removeLocations(String key, T... members) {
        GeoOperations<String, T> geoOps = redisTemplate.opsForGeo();
        return geoOps.remove(key, members);
    }

    /**
     * 将 Metrics 转换为 RedisGeoCommands.DistanceUnit
     *
     * @param metrics Metrics 对象
     * @return 对应的 DistanceUnit
     */
    private RedisGeoCommands.DistanceUnit convertMetricsToDistanceUnit(Metrics metrics) {
        switch (metrics) {
            case METERS:
                return RedisGeoCommands.DistanceUnit.METERS;
            case KILOMETERS:
                return RedisGeoCommands.DistanceUnit.KILOMETERS;
            case MILES:
                return RedisGeoCommands.DistanceUnit.MILES;
            case FEET:
                return RedisGeoCommands.DistanceUnit.FEET;
            default:
                return RedisGeoCommands.DistanceUnit.METERS;
        }
    }
}
